---
title: Step 9 - Scoring Points
slug: /step-9-scoring-points
section: Tutorial
---

# Step 9 - Scoring Points!

Any good game needs points, so let's add some!

First we'll add a score label and best score to our `Level` to keep track of the current score for us. Additionally we'll add an `incrementScore()` to up the value. We can use the browser's `localStorage` feature to keep track of our best score.

```typescript
// level.ts
export class Level extends ex.Scene {
    ...
    score: number = 0;
    best: number = 0;
    scoreLabel = new ex.Label({
        text: 'Score: 0',
        x: 0,
        y: 0,
        z: 1,
        font: new ex.Font({
            size: 20,
            color: ex.Color.White
        })
    });

    bestLabel = new ex.Label({
        text: 'Best: 0',
        x: 400,
        y: 0,
        z: 1,
        font: new ex.Font({
            size: 20,
            color: ex.Color.White,
            textAlign: ex.TextAlign.End
        })
    });

    onInitialize(engine: ex.Engine): void {
        ...

        this.add(this.scoreLabel);
        this.add(this.bestLabel);

        const bestScore = localStorage.getItem('bestScore');
        if (bestScore) {
            this.best = +bestScore;
            this.setBestScore(this.best);
        } else {
            this.setBestScore(0);
        }
    }

    incrementScore() {
        this.scoreLabel.text = `Score: ${++this.score}`;
        this.setBestScore(this.score);
    }

    setBestScore(score: number) {
        if (score > this.best) {
            localStorage.setItem('bestScore', this.score.toString());
            this.best = score;
        }
        this.bestLabel.text = `Best: ${this.best}`;
    }
}
```

When our `Bird` flies between two pipes we want to increment the score. To do this will create a new file `score-trigger.ts` to detect this.

```typescript
// score-trigger.ts
export class ScoreTrigger extends ex.Actor {
    constructor(pos: ex.Vector, private level: Level) {
        super({
            pos,
            width: 32,
            height: Config.PipeGap,
            anchor: ex.vec(0, 0),
            vel: ex.vec(-Config.PipeSpeed, 0)
        })

        this.on('exitviewport', () => {
            this.kill();
        });
    }

    override onCollisionStart(): void {
        this.level.incrementScore();
    }
}
```

Now in our pipe factory we put these triggers in between our pipes, and add `ScoreTrigger` to our `reset()` and `stop()` routines


```typescript
// pipe-factory.ts

export class PipeFactory {
    ...

    spawnPipes() {
        ...

        const scoreTrigger = new ScoreTrigger(
            ex.vec(
                this.level.engine.screen.drawWidth,
                randomPipePosition),
            this.level
        );
        this.level.add(scoreTrigger);

    }

    ...
    reset() {
        for (const actor of this.level.actors) {
            if (actor instanceof Pipe ||
                actor instanceof ScoreTrigger
            ) {
                actor.kill();
            }
        }
    }

    stop() {
        this.timer.stop();
        for (const actor of this.level.actors) {
            if (actor instanceof Pipe ||
                actor instanceof ScoreTrigger
            ) {
                actor.vel = ex.vec(0, 0);
            }
        }
    }
}
```


You might notice that sometimes the score double counts sometimes! Well this is because the bird is using a box collider and is rotating into the score trigger! This is an easy fix, we'll switch to a circle collider in the bird.


```typescript
// bird.ts

export class Bird extends ex.Actor {
    ...
    constructor() {
        super({
            pos: Config.BirdStartPos,
            // width: 16,
            // height: 16,
            radius: 8,
            color: ex.Color.Yellow
        });
    }
  ...
}

```
